package com.github.luoshu.open.shiro.redis;

import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.mgt.ValidatingSession;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.Objects;

/**
 * @author inme
 */
public class RedisSession implements ValidatingSession {
    // session 创建时间
    public static String SESSION_CREATE_TIME = "__SESSION_CREATE_TIME__";
    // session 最后访问时间
    public static String SESSION_LAST_ACCESS_TIME = "__SESSION_LAST_ACCESS_TIME__";
    // 存储 session 的 host
    public static String SESSION_HOST = "__SESSION_HOST__";
    // session 的会话时间，单位分钟
    public static String SESSION_TIMEOUT_MINUTE = "__SESSION_TIMEOUT_MINUTE__";


    private String sessionId;
    private RedisManager redisManager;
    // session 的过期时间，分钟
    private Integer sessionTimeMinute;

    private String host;


    public RedisSession(String sessionId, RedisManager redisManager, Integer sessionTimeMinute) {
        this.sessionId = sessionId;
        this.redisManager = redisManager;
        this.sessionTimeMinute = sessionTimeMinute;
    }

    /**
     * 获取 session 的最大超时时间，也就是这个session会话，应该存在的时间
     * @return
     */
    public int getSessionTimeMinute() {
        if(sessionTimeMinute == null){
            String sessionKey = getRedisSessionKey(sessionId);
            sessionTimeMinute = redisManager.getHash(sessionKey , SESSION_TIMEOUT_MINUTE);
        }
        return sessionTimeMinute;
    }

    @Override
    public Serializable getId() {
        return sessionId;
    }

    @Override
    public Date getStartTimestamp() {
        String key = getRedisSessionKey(sessionId);
        Date date = redisManager.getHash(key , SESSION_CREATE_TIME);
        return date;
    }

    @Override
    public Date getLastAccessTime() {
        String key = getRedisSessionKey(sessionId);
        Date date = redisManager.getHash(key , SESSION_LAST_ACCESS_TIME);
        return date;
    }

    @Override
    public long getTimeout() throws InvalidSessionException {
        return sessionTimeMinute * 60 * 1000;
    }

    @Override
    public void setTimeout(long maxIdleTimeInMillis) throws InvalidSessionException {
        this.sessionTimeMinute = (int)(maxIdleTimeInMillis / 60 / 1000);
        String sessionKey = getRedisSessionKey(sessionId);
        this.redisManager.expire(sessionKey , getTimeout());
    }

    @Override
    public String getHost() {
        if(host == null){
            String sessionKey = getRedisSessionKey(sessionId);
            host = redisManager.getHash(sessionKey , SESSION_HOST);
        }
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    @Override
    public void touch() throws InvalidSessionException {
        // 当调用此方法时，显式地将此会话的lastAccessTime更新为当前时间。此方法可用于确保会话不会超时。

        // TODO: 在一个请求内，会多次调用，为了提高性能，其实一次请求，触发一次redis刷新就可以了
        String key = getRedisSessionKey(sessionId);
        redisManager.putHash(key , SESSION_LAST_ACCESS_TIME , System.currentTimeMillis());
        redisManager.expire(key , getSessionTimeMinute() * 60 * 1000);
    }

    @Override
    public void stop() throws InvalidSessionException {
        // 注销此会话
        String key = getRedisSessionKey(sessionId);
        redisManager.deleteKey(key);
    }

    @Override
    public Collection<Object> getAttributeKeys() throws InvalidSessionException {
        String key = getRedisSessionKey(sessionId);
        return redisManager.getHashKeys(key);
    }

    @Override
    public Object getAttribute(Object key) throws InvalidSessionException {
        String sessionKey = getRedisSessionKey(sessionId);
        return redisManager.getHash(sessionKey , key);
    }

    @Override
    public void setAttribute(Object key, Object value) throws InvalidSessionException {
        String sessionKey = getRedisSessionKey(sessionId);
        redisManager.putHash(sessionKey , key , value );
    }

    @Override
    public Object removeAttribute(Object key) throws InvalidSessionException {
        Object obj = getAttribute(key);

        String sessionKey = getRedisSessionKey(sessionId);
        redisManager.deleteHash(sessionKey , key);
        return obj;
    }

    public static String getRedisSessionKey(String sessionId){
        if(sessionId == null || "".equals(sessionId.trim())){
            throw new RuntimeException("sessionId is null");
        }
        return "luoshu:session:" + sessionId;
    }

    @Override
    public boolean isValid() {
        // 判断当前的 session 是否有效
        // 如果有效， 返回 true
        String key = getRedisSessionKey(sessionId);
        boolean r = redisManager.hasKey(key);
        return r;
    }

    @Override
    public void validate() throws InvalidSessionException {
        if(!isValid()){
            throw new InvalidSessionException("session is invalid sessionId : " + sessionId);
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RedisSession that = (RedisSession) o;
        return Objects.equals(sessionId, that.sessionId);
    }

    @Override
    public int hashCode() {
        return sessionId.hashCode();
    }
}
